﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Win32;

using TibiaMC;

namespace TibiaUniversalMC
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            LoadSettings();
            
            this.recent.ItemsSource = this.listBox.Items;
        }

        #region Dependency Variables
        public string LastFileName
        {
            get { return (string)GetValue(LastFileNameProperty); }
            set { SetValue(LastFileNameProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LastFileName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LastFileNameProperty =
            DependencyProperty.Register("LastFileName", typeof(string), typeof(MainWindow), new UIPropertyMetadata(null));
        #endregion

        #region Constants
        private const byte maxEntries = 10;
        private const string tibiaClassName = "TibiaClient";
        private const string tibiaProductName = "Tibia Player";
        private const string configFileName = "TibiaMC.txt";
        private const string tibiaFileError = "Tibia Client expected";
        #endregion

        #region P/Invoke
        private static readonly IntPtr NULL = IntPtr.Zero;

        [DllImport("user32.dll")]
        private static extern IntPtr FindWindow(string className, string windowTitle);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern int GetWindowThreadProcessId(IntPtr windowHandle, out int processId);
        #endregion

        #region Override (OnClosing -> Save settings)
        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            SaveSettings();
        }
        #endregion

        #region Private Methods
        private void SaveSettings()
        {
            using (StreamWriter writer = File.CreateText(configFileName))
            {
                string lastFileName = this.LastFileName;
                if (lastFileName == null)
                    return;

                writer.WriteLine(lastFileName);

                string fileName;
                ItemCollection items = this.listBox.Items;

                int count = items.Count;
                for (int i = 0; i < count; i++)
                {
                    fileName = (string)(items[i]);

                    writer.WriteLine(fileName);
                }
            }
        }
        private void LoadSettings()
        {
            if (!File.Exists(configFileName))
                return;

            try
            {
                using (StreamReader reader = File.OpenText(configFileName))
                {
                    string fileName;
                    ItemCollection items = this.listBox.Items;

                    if ((fileName = reader.ReadLine()) != null)
                    {
                        TryLoad(fileName, items);
                        this.LastFileName = fileName;

                        byte i = 0;
                        while ((fileName = reader.ReadLine()) != null && i++ < maxEntries)
                            TryLoad(fileName, items);
                    }
                }
            }
            catch (Exception ex)
            {
                ShowException(ex);
            }
        }
        private void TryLoad(string fileName, ItemCollection items)
        {
            if (File.Exists(fileName))
            {
                FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(fileName);

                if (fileVersionInfo.ProductName == tibiaProductName)
                {
                    if (items.Contains(fileName))
                        return;

                    items.Add(fileName);
                }
                else
                {
                    throw new Exception("Tibia Client expected");
                }
            }
            else
            {
                throw new Exception("Tibia Client expected");
            }
        }
        private void ShowException(Exception ex)
        {
            ShowError(ex.Message);
        }
        private void ShowError(string error)
        {
            MessageBox.Show(error, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        }
        private void LaunchClient(string fileName)
        {
            MC.Open(fileName);

            ItemCollection items = this.listBox.Items;

            if (items.Contains(fileName))
                return;

            items.Insert(0, fileName);

            int count = items.Count;
            if (count > maxEntries)
                items.RemoveAt(count - 1);

            this.LastFileName = fileName;
        }
        private bool TryGetFileName(string directory, out string fileName)
        {
            foreach (string file in Directory.EnumerateFiles(directory))
            {
                FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(file);

                if (fileVersionInfo.ProductName == tibiaProductName)
                {
                    fileName = file;
                    return true;
                }
            }

            fileName = null;
            return false;
        }
        #endregion

        #region Event Handlers
        private void run_Click(object sender, RoutedEventArgs e)
        {
            string selected = (string)(this.listBox.SelectedItem);
            try
            {
                if (selected == null)
                {
                    string fileName = this.LastFileName;

                    if (fileName == null)
                    {
                        string directory = Path.GetDirectoryName(App.Current.StartupUri.AbsolutePath);
                        if (TryGetFileName(directory, out fileName))
                        {
                            LaunchClient(fileName);
                        }
                        else
                        {
                            IntPtr windowHandle = FindWindow(tibiaClassName, null);
                            if (windowHandle == NULL)
                            {
                                ItemCollection items = this.listBox.Items;
                                if (items.Count > 0)
                                {
                                    fileName = (string)(items[0]);
                                    try
                                    {
                                        MC.Open(fileName);

                                        this.LastFileName = fileName;
                                    }
                                    catch (Exception ex)
                                    {
                                        ShowException(ex);
                                    }
                                }
                                else
                                {
                                    directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Tibia");
                                    if (Directory.Exists(directory) && TryGetFileName(directory, out fileName))
                                    {
                                        LaunchClient(fileName);
                                    }
                                    else
                                    {
                                        directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Tibia");
                                        if (Directory.Exists(directory) && TryGetFileName(directory, out fileName))
                                        {
                                            LaunchClient(fileName);
                                        }
                                        else
                                        {
                                            MessageBox.Show("Choose Tibia through 'Run from ...' button", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                int processId;
                                if (GetWindowThreadProcessId(windowHandle, out processId) == 0)
                                {
                                    int lastError = Marshal.GetLastWin32Error();
                                    throw new Win32Exception(lastError);
                                }

                                Process process = Process.GetProcessById(processId);
                                ProcessModule mainModule = process.MainModule;
                                try
                                {
                                    fileName = mainModule.FileName;

                                    LaunchClient(fileName);
                                }
                                catch (Exception ex)
                                {
                                    ShowException(ex);
                                }
                                finally
                                {
                                    mainModule.Dispose();
                                    process.Dispose();
                                }
                            }
                        }
                    }
                    else
                    {
                        MC.Open(fileName);
                    }
                }
                else
                {
                    MC.Open(selected);
                    this.LastFileName = selected;
                }
            }
            catch (Exception ex)
            {
                ShowException(ex);
            }
        }
        private void runFrom_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Tibia|*.exe";
            ofd.CheckFileExists = true;

            if (!ofd.ShowDialog().Value)
                return;

            string fileName = ofd.FileName;

            FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(fileName);
            if (fileVersionInfo.ProductName == tibiaProductName)
            {
                try
                {
                    LaunchClient(fileName);
                }
                catch (Exception ex)
                {
                    ShowException(ex);
                }
            }
            else
            {
                ShowError(tibiaFileError);
            }
        }
        private void recent_Click(object sender, RoutedEventArgs e)
        {
            MenuItem item = (MenuItem)e.OriginalSource;
            string fileName = item.Header.ToString();

            try
            {
                MC.Open(fileName);
            }
            catch (Exception ex)
            {
                ShowException(ex);
            }

            e.Handled = true;
        }
        private void exit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }
        private void howToUse_Click(object sender, RoutedEventArgs e)
        {
            foreach (Window w in App.Current.Windows)
            {
                if (w.Name == "howToUse")
                {
                    w.Focus();
                    return;
                }
            }

            Window window = new Window();
            window.Title = "How to use";
            window.Name = "howToUse";
            window.Width = 350;
            window.Height = 300;
            window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
            RichTextBox rtb = new RichTextBox();
            rtb.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            rtb.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;

            string msg = string.Concat(
                "The 'Recent' list in the main screen displays the Tibia executable paths used for you so far (max ",
                maxEntries.ToString(),
@" entries), allowing you to delete (through 'Delete' button) any entry if you don't plan to run clients from that location anymore.

Click the 'Run from ...' button to browse your Tibia executable (Tibia.exe), thus this program will launch a new Tibia instance from there. Otherwise, you can simply click 'Run' and this program will try to find out any Tibia already running to launch a new instance from the same location.

Have fun!
");
            rtb.Document.ContentStart.InsertTextInRun(msg);
            window.Content = rtb;
            window.Show();
        }
        private void hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
        {
            Process.Start(e.Uri.AbsoluteUri);
        }
        private void delete_Click(object sender, RoutedEventArgs e)
        {
            string selected = (string)(this.listBox.SelectedItem);
            if (selected == null)
            {
                MessageBox.Show("You must select an entry", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
            }
            else
            {
                if (MessageBox.Show(string.Format("Are you sure to delete '{0}'?", selected), "Question", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
                    return;

                this.listBox.Items.Remove(selected);
            }
        }
        #endregion
    }
}
